#ifndef SQLPARSER_SELECT_STATEMENT_H
#define SQLPARSER_SELECT_STATEMENT_H

#include "SQLStatement.h"
#include "Expr.h"
#include "Table.h"

namespace hsql {
  enum OrderType {
    kOrderAsc,
    kOrderDesc
  };

  enum SetType {
    kSetUnion,
    kSetIntersect,
    kSetExcept
  };

  // Description of the order by clause within a select statement.
  struct OrderDescription {
    OrderDescription(OrderType type, Expr* expr);
    virtual ~OrderDescription();

    OrderType type;
    Expr* expr;
  };

  // Description of the limit clause within a select statement.
  struct LimitDescription {
    LimitDescription(Expr* limit, Expr* offset);
    virtual ~LimitDescription();

    Expr* limit;
    Expr* offset;
  };

  // Description of the group-by clause within a select statement.
  struct GroupByDescription {
    GroupByDescription();
    virtual ~GroupByDescription();

    std::vector<Expr*>* columns;
    Expr* having;
  };

  struct WithDescription {
      ~WithDescription();

      char* alias;
      SelectStatement* select;
  };

  struct SetOperation {
    SetOperation();
    virtual ~SetOperation();

    SetType setType;
    bool isAll;

    SelectStatement* nestedSelectStatement;
    std::vector<OrderDescription*>* resultOrder;
    LimitDescription* resultLimit;

  };

  // Representation of a full SQL select statement.
  struct SelectStatement : SQLStatement {
    SelectStatement();
    ~SelectStatement() override;

    TableRef* fromTable;
    bool selectDistinct;
    std::vector<Expr*>* selectList;
    Expr* whereClause;
    GroupByDescription* groupBy;

    // Note that a SetOperation is always connected to a
    // different SelectStatement. This statement can itself
    // have SetOperation connections to other SelectStatements.
    // To evaluate the operations in the correct order:
    //    Iterate over the setOperations vector: 
    //      1. Fully evaluate the nestedSelectStatement within the SetOperation
    //      2. Connect the original statement with the 
    //         evaluated nestedSelectStatement
    //      3. Apply the resultOrder and the resultLimit
    //      4. The result now functions as the the original statement
    //         for the next iteration
    //    
    // Example:
    //
    //   (SELECT * FROM students INTERSECT SELECT * FROM students_2) UNION SELECT * FROM students_3 ORDER BY grade ASC;
    //
    //   1. We evaluate `Select * FROM students`
    //   2. Then we iterate over the setOperations vector
    //   3. We evalute the nestedSelectStatement of the first entry, which is: `SELECT * FROM students_2`
    //   4. We connect the result of 1. with the results of 3. using the setType, which is INTERSECT
    //   5. We continue the iteration of the setOperations vector
    //   6. We evaluate the new nestedSelectStatement which is: `SELECT * FROM students_3`
    //   7. We apply a Union-Operation to connect the results of 4. and 6.
    //   8. Finally, we apply the resultOrder of the last SetOperation (ORDER BY grade ASC)
    std::vector<SetOperation*>* setOperations;

    std::vector<OrderDescription*>* order;
    std::vector<WithDescription*>* withDescriptions;
    LimitDescription* limit;
  };


} // namespace hsql

#endif
